package com.flute.icrawler.app.util;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class HtmlParserImp implements IParser {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(HtmlParserImp.class);

	private Document document = null;

	private static XPath XPATH = XPathFactory.newInstance().newXPath();

	private AbstractParserDocment parserDocment = null;

	private String xpath = "";

	public HtmlParserImp(AbstractParserDocment parserDocment, String xpath) {
		try {
			this.parserDocment = parserDocment;
			this.document = parserDocment.getDocument();
			this.xpath = xpath;
		} catch (ParserException e) {
			LOGGER.error("HtmlParserImp:{}", e);
		}
	}

	/**
	 * get the html of the docment
	 * 
	 * @return
	 */
	public String getHTMLContent() {

		StringBuffer buffer = new StringBuffer();
		this.transform(this.document, buffer);
		return buffer.toString().replaceAll("(\r\n|\n\r){2,}", "\r\n");

	}

	/**
	 * get the html of the node(include all children nodes)
	 * 
	 * @return
	 */
	public String[] getHTML() {
		List<String> list = new ArrayList<String>();
		NodeList nodes = null;
		try {
			nodes = (NodeList) XPATH.evaluate(xpath, document,
					XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			LOGGER.error("HtmlParserImp:{}", e);
		}
		int len = nodes.getLength();
		for (int i = 0; i < len; i++) {
			Node node = nodes.item(i);
			StringWriter writer = new StringWriter();
			try {
				javax.xml.transform.TransformerFactory
						.newInstance()
						.newTransformer()
						.transform(new DOMSource(node),
								new StreamResult(writer));
				list.add(writer
						.toString()
						.replaceAll(
								"\\<\\?xml version=\\\"1.0\\\"\\s*encoding=\\\".*?\\\"\\?\\>",
								"").replaceAll("(\r\n|\n\r){2,}", "\r\n"));
			} catch (Exception e) {
				LOGGER.error("HtmlParserImp:{}", e);
			} finally {
				try {
					writer.close();
				} catch (IOException e) {
					LOGGER.error("HtmlParserImp:{}", e);
				}
			}

		}
		return list.toArray(new String[] {});
	}

	/**
	 * get content by xpath
	 * 
	 * @param path
	 * @return
	 */
	public String[] getXPATHContent() {
		List<String> list = new ArrayList<String>();

		NodeList nodes = null;
		try {
			nodes = (NodeList) XPATH.evaluate(xpath, document,
					XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			LOGGER.error("HtmlParserImp:{}", e);
			return new String[] {};
		}

		int len = nodes.getLength();
		for (int i = 0; i < len; i++) {
			Node node = nodes.item(i);

			StringBuffer buffer = new StringBuffer("");

			this.getNodeValue(node, buffer);

			if (buffer.toString().trim().equals("")) {
				continue;
			}

			list.add(buffer.toString());

		}
		return list.toArray(new String[0]);
	}

	public String[] getImgs() throws ParserException {
		List<String> list = new ArrayList<String>();

		NodeList nodes = null;
		try {
			nodes = (NodeList) XPATH.evaluate("//IMG", document,
					XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			LOGGER.error("HtmlParserImp:{}", e);
			return null;
		}

		int len = nodes.getLength();
		for (int i = 0; i < len; i++) {
			Element element = (Element) nodes.item(i);
			String src = element.getAttribute("src");
			if (src == null || src.trim().equals("")) {
				continue;
			}
			list.add(src);

		}
		return list.toArray(new String[0]);
	}

	/**
	 * 获取包含的所有链接
	 */
	public String[] getLinks() throws ParserException {
		HashSet<String> set = new HashSet<String>();

		NodeList nodes = null;
		try {
			nodes = (NodeList) XPATH.evaluate("//A|//IFRAME|//FRAME", document,
					XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			LOGGER.error("HtmlParserImp:{}", e);
			return null;
		}

		XPATH.reset();
		int len = nodes.getLength();
		for (int i = 0; i < len; i++) {
			String uri = "";

			Element element = (Element) nodes.item(i);

			String tag = element.getNodeName();
			if (tag.equals("A")) {
				uri = element.getAttribute("href");
			} else {
				uri = element.getAttribute("src");
			}

			if (uri == null || uri.trim().equals("") || uri.equals("#")) {
				continue;
			}
			uri = this.fixURL(uri.trim());

			// 先看是否能满足时合法的URL
			// if (!isNormalURL(uri)) {
			// return "";
			// }

			LOGGER.debug("uri->" + uri);
			if (uri != null && !uri.equals("")
			// && isNormalURL(uri)
					&& !set.contains(uri)) {
				set.add(uri);
			} else {
				continue;
			}

		}

		nodes = null;

		return set.toArray(new String[0]);
	}

	public String[] getNodesAttribute(String attribute) throws ParserException {
		HashSet<String> set = new HashSet<String>();

		NodeList nodes = null;
		try {
			nodes = (NodeList) XPathFactory.newInstance().newXPath()
					.evaluate(xpath, document, XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			LOGGER.error("HtmlParserImp:{}", e);
			return null;
		}

		int len = nodes.getLength();
		for (int i = 0; i < len; i++) {
			Element element = (Element) nodes.item(i);
			String atrr = element.getAttribute(attribute);
			if (atrr == null || atrr.trim().equals("")) {
				continue;
			}

			set.add(atrr);
		}
		return new ArrayList<String>(set).toArray(new String[0]);
	}

	/**
	 * 获得带协议头完整的URL链接
	 * 
	 * @param uri
	 * @return
	 */
	private String fixURL(String uri) {

		if (uri.startsWith("http")) {
			// 不需要做操作 等待后续的正则匹配
		} else if (uri.startsWith("www.")) {
			uri = "http://" + uri;
		} else if (uri.startsWith("/")) {// 以/开头说明是内部资源链接

			Pattern p = Pattern.compile("http://(\\w+\\.)+\\w+/?");
			Matcher m = p.matcher(parserDocment.getSourceName());
			if (m.find()) {
				uri = m.group() + uri;
			}
		} else {
			uri = parserDocment.getSourceName().substring(0,
					(parserDocment.getSourceName()).lastIndexOf("/") + 1)
					+ uri;
		}

		// 将最后一个斜杠去掉
		int length = uri.length();
		if (uri.lastIndexOf("/") == length - 1) {
			uri = uri.substring(0, length - 1);
		}
		// 替换里面的斜杠
		uri = uri.replaceAll("//", "/");
		uri = uri.replaceFirst("/", "//");
		uri = uri.replaceAll(" ", "%20");

		try {
			uri = replaceChinese(uri);
		} catch (Exception e) {
			LOGGER.error("HtmlParserImp:{}", e);
		}

		return uri;

	}

	/**
	 * 替换掉链接里的中文
	 * 
	 * @param url
	 *            输入的链接
	 * @return 替换后的链接
	 * @throws Exception
	 */
	private String replaceChinese(String url) throws Exception {
		LOGGER.debug("before transfer url=" + url);
		StringBuffer buffer = new StringBuffer();
		char[] cs = url.toCharArray();
		for (char c : cs) {
			String value = String.valueOf(c);
			if (!value.matches("[0-9a-zA-Z,\\.,\\:,\\/,\\*,-,_]")) {
				buffer.append(java.net.URLEncoder.encode(value, "gb2312"));
			} else {
				buffer.append(value);
			}
		}

		LOGGER.debug("after transfer url=" + buffer.toString());
		return buffer.toString();
	}

	/**
	 * @param node
	 * @param buffer
	 */
	private void getNodeValue(Node node, StringBuffer buffer) {

		if (node.getNodeType() == Node.TEXT_NODE) {
			buffer.append(node.getNodeValue());

		} else if (node.getNodeType() == Node.ELEMENT_NODE) {
			Element element = (Element) node;
			String tag = element.getTagName().toLowerCase();

			if (!tag.equals("script") && !tag.equals("style")
					&& !tag.equals("link") && !tag.equals("iframe")
					&& !tag.equals("frame")) {
				if (node.hasChildNodes()) {
					NodeList nodes = node.getChildNodes();
					for (int i = 0; i < nodes.getLength(); i++) {
						Node child = nodes.item(i);
						this.getNodeValue(child, buffer);
					}
				}
			}
		} else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
			String text = node.getNodeValue();
			if (text != null) {
				buffer.append(text);
			}
		}
	}

	/**
	 * transform DOM to String
	 * 
	 * @param node
	 * @param buffer
	 */
	private void transform(Node node, StringBuffer buffer) {
		if (node.getNodeType() == Node.TEXT_NODE) {
			if (node.getNodeValue() != null && !node.getNodeValue().equals("")) {
				buffer.append(node.getNodeValue());
			}
		} else if (node.getNodeType() == Node.ELEMENT_NODE) {
			String tag = node.getNodeName().toUpperCase();
			// 过滤不需要的节点（eg.广告节点�?��在iframe,embed中）
			if (tag.equals("IFRAME") || tag.equals("FREAME")
					|| tag.equals("SCRIPT") || tag.equals("STYLE")
					|| tag.equals("LINK") || tag.equals("NOSCRIPT")
					|| tag.equals("EMBED")) {
				return;
			}
			buffer.append("<").append(tag);

			NamedNodeMap attrs = node.getAttributes();
			for (int i = 0; i < attrs.getLength(); i++) {
				Node attr = attrs.item(i);
				buffer.append(" ").append(attr.getNodeName().toLowerCase())
						.append("='").append(attr.getNodeValue()).append("'");
			}
			buffer.append(">");

			if (node.hasChildNodes()) {
				NodeList nodes = node.getChildNodes();
				for (int j = 0; j < nodes.getLength(); j++) {
					this.transform(nodes.item(j), buffer);
				}
			}

			buffer.append("</").append(tag).append(">");

		} else if (node.getNodeType() == Node.DOCUMENT_NODE) {
			this.transform(((Document) node).getDocumentElement(), buffer);
		}
	}

	public static void main(String[] args) {
		Pattern p = Pattern.compile("http://(\\w+\\.)+\\w+/?");
		Matcher m = p.matcher("http://music.douban.com/sasa");
		if (m.find()) {
			System.out.println(m.group());
		}

		// 替换里面的斜杠
		String uri = "http://sdfd.dfd.com/";
		uri = uri.replaceAll("//", "/");
		uri = uri.replaceFirst("/", "//");
		int length = uri.length();
		if (uri.lastIndexOf("/") == length - 1) {
			uri = uri.substring(0, length - 1);
		}
		System.out.println(uri);
	}

	@Override
	public String getTextContent() {

		return document.getTextContent();
	}
}
